import Reporter from './reporter';
import Storage from './storage';
import { LData, Task, PriorityEnum } from './type';
import eventEmitter from './eventEmitter';

export interface Config {
  client: Reporter;
  storage: Storage;
}

/**
 * 调度器，定期检查缓冲区，根据任务优先级决定立即上报、超容上报、或定时上报
 *
 * @export
 * @class Schedule
 */
export default class Scheduler {
  maxTasks: number;

  client: Reporter;

  storage: Storage;

  timer: any;

  // 周期上报时间间隔
  cycleTime: number;

  constructor(config: Config) {
    const { client, storage } = config;
    this.client = client;
    this.storage = storage;
    this.maxTasks = this.storage.maxTasks;
    this.cycleTime = 5;
    this.timer = null;

    this.start();

    eventEmitter.on(PriorityEnum.LOW, this.monitorBuffer.bind(this));
    eventEmitter.on(PriorityEnum.HIGH, this.uploadHighPriorityTask.bind(this));
  }

  /**
   * 开始执行定时任务
   *
   * @memberof Schedule
   */
  start() {
    if (!this.timer) {
      this.timer = setInterval(() => {
        this.uploadBufferedTasks();
      }, this.cycleTime * 1000);
    }
  }

  /**
   * 停止执行定时任务
   *
   * @memberof Schedule
   */
  stop() {
    if (this.timer) {
      clearInterval(this.timer);
      this.timer = null;
    }
  }

  // 立即上传高优先级任务
  async uploadHighPriorityTask(task: LData[]) {
    await this.consume(task, PriorityEnum.HIGH);
  }

  // 执行低优先级任务的上传策略
  async uploadBufferedTasks() {
    if (!this.storage.hasTasks()) return;
    const tasks = this.storage.pop(this.maxTasks);
    await this.consume(tasks);
    if (this.storage.hasTasks()) {
      setTimeout(() => {
        this.uploadBufferedTasks();
      }, 100);
    }
  }

  // 监听缓冲区的任务情况
  monitorBuffer() {
    if (this.storage.isFull()) {
      this.uploadBufferedTasks(); // 缓冲区超容，立即上传
    }
  }

  /**
   * 任务消费
   *
   * @param {boolean} [clear] 是否清空
   * @return {*}
   * @memberof Schedule
   */
  async consume(tasks: Task[] | LData[], priority = PriorityEnum.LOW) {
    const data = priority === PriorityEnum.LOW ? tasks.map(({ data }) => data) : tasks;
    try {
      await this.client.send(
        { ...this.client.baseData, list: data },
        {
          url: `${this.client?.config?.dsn}/report`,
        },
      );
    } catch (error) {
      console.log(error);
    }
  }
}
